#pragma once

#include <algorithm>
#include <jsoncpp/json/json.h>
#include <iostream>
#include <string>
#include "log.hpp"
#include "index.hpp"
#include "util.hpp"

namespace ns_searcher
{
    //for output to usrs
    struct InvertedElemPrint
    {
        uint64_t _doc_id;
        int _weight; //累加相同文件的权值
        std::vector<std::string> _words;
        
        InvertedElemPrint()
            :_doc_id(0), _weight(0)
        {}
    };

    class Searcher
    {
    public:
        Searcher() {};

        void InitSearcher(const std::string& input)
        {
            //获取index对象
            _index = ns_index::Index::getInstance();
            std::cout << "获取index对象成功..." << std::endl;
            ns_log::lg(ns_log::LOG, "获取index对象成功...");
            //清空索引
            _index->clearIndex();
            std::cout << "\n清空索引成功..." << std::endl;
            ns_log::lg(ns_log::LOG, "清空索引成功...");
            //建立索引
            _index->buildIndex(input);
            std::cout << "\n建立索引成功..." << std::endl;
            ns_log::lg(ns_log::LOG, "建立索引成功...");
        }

        void Search(const std::string& query, std::string* json_str)
        {
            //1.分词
            std::vector<std::string> words;
            ns_util::JiebaUtil::CutString(query, &words);
            //2.触发
            // ns_index::InvertedList InvertedListAll;
            //文档去重
            std::unordered_map<uint64_t, InvertedElemPrint> tokens_map; //doc_id - InvertedElemPrint
            std::vector<InvertedElemPrint> InvertedListAll;

            //解决：可能会将相同的文档纳入InvertedListAll
            for(auto& word : words)
            {
                //进行词频统计时一样 查找时也忽略大小写
                std::string wd = std::move(ns_util::StringUtil::toLower(word));
                ns_index::InvertedList* list = _index->getInvertedList(wd);
                if(list == nullptr) {
                    ns_log::lg(ns_log::WARNING, "%s: not found", word.c_str());
                    continue;
                }
                //遍历list去重
                for(const auto& elem : *list)
                {
                    auto& item = tokens_map[elem._doc_id];
                    item._doc_id = elem._doc_id;
                    item._weight += elem._weight;
                    item._words.push_back(elem._keyword);
                }
                //3.合并
                // InvertedListAll.insert(InvertedListAll.end(), list->begin(), list->end());
            }
            //3.合并
            for(const auto& item : tokens_map) {
                InvertedListAll.push_back(std::move(item.second));
            }
            //4.排序
            // std::sort(InvertedListAll.begin(), InvertedListAll.end(), \
            // [](const ns_index::InvertedElem& e1, const ns_index::InvertedElem& e2) { \
            //     return e1._weight > e2._weight; });
            std::sort(InvertedListAll.begin(), InvertedListAll.end(), \
            [](const InvertedElemPrint& e1, const InvertedElemPrint& e2) { \
                return e1._weight > e2._weight; });
            //5.构建json串
            Json::Value root;
            for(const auto& Elem : InvertedListAll)
            {
                Json::Value json_elem;
                ns_index::DocInfo* doc = _index->getForwardIndex(Elem._doc_id);
                if(doc == nullptr) { continue; }
                
                json_elem["title"] = doc->_title;
                json_elem["desc"] = BuildDesc(doc->_content, Elem._words[0]); //建立文档摘要
                json_elem["url"] = doc->_url;
                root.append(json_elem);
            }
            // Json::StyledWriter writer; //for debug
            Json::FastWriter writer;
            *json_str = writer.write(root);
        }

        ~Searcher() {};

    private:
        std::string BuildDesc(const std::string& content, const std::string& keyword)
        {
            const int pre_size = 50;
            const int back_size = 100;
            //忽略大小写进行查找
            auto iter = std::search(content.begin(), content.end(), keyword.begin(), keyword.end(),
                [](int e1, int e2) { return std::tolower(e1) == std::tolower(e2); });
            
            if(iter == content.end()) { return "None"; }
            int pos = std::distance(content.begin(), iter);

            int start = 0;
            int end = content.size() - 1;
            if(pos - pre_size > start) {
                start = pos - pre_size;
            }
            if(pos + back_size < end) {
                end = pos + back_size;
            }
            if(start >= end) { return "None"; }
            std::string cont = "... ";
            cont += content.substr(start, end - start);
            cont += " ...";
            return cont;
        }

    private:
        ns_index::Index* _index;
    };
}